package genetic.q1;

import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import java.util.Map.Entry;

import genetic.q1.Genotype;
import genetic.q1.functions.ListVariable;
import genetic.q1.functions.Operator;
import genetic.q1.functions.Program;
import genetic.q1.functions.Statement;
import genetic.q1.functions.Variable;
import genetic.q1.functions.sumlist.operators.Equal;
import genetic.q1.functions.sumlist.operators.Less;
import genetic.q1.functions.sumlist.operators.LessOrEqual;
import genetic.q1.functions.sumlist.operators.More;
import genetic.q1.functions.sumlist.operators.MoreOrEqual;
import genetic.q1.functions.sumlist.operators.NotEqual;
import genetic.q1.functions.sumlist.statements.Add;
import genetic.q1.functions.sumlist.statements.Assign;
import genetic.q1.functions.sumlist.statements.Divide;
import genetic.q1.functions.sumlist.statements.End;
import genetic.q1.functions.sumlist.statements.For;
import genetic.q1.functions.sumlist.statements.If;
import genetic.q1.functions.sumlist.statements.Multiply;
import genetic.q1.functions.sumlist.statements.Null;
import genetic.q1.functions.sumlist.statements.Subtract;

public class SumlistPhenotype extends WithallPhenotype {

	int maxBlocks;
	int[] examples;
	HashMap<int[], Integer> samples;

	public SumlistPhenotype(int numBlocks, int maxBlocks, Vector<int[]> samples) {
		super(numBlocks, 4, 0, 17, getStatements(), getVarpack(), getComparisons());
		this.maxBlocks = maxBlocks;
		processSamples(samples);
	}


	private void processSamples(Vector<int[]> in) {
		samples = new HashMap<int[], Integer>();
		for (int[] list : in){
			int sum = 0;
			for (int j=0; j<list.length; j++){
				sum+=list[j];
			}
			samples.put(list, sum);
		}
	}


	private static Operator[] getComparisons() {
		Operator[] comparisons = new Operator[6];
		comparisons[0] = new Equal();
		comparisons[1] = new Less();
		comparisons[2] = new LessOrEqual();
		comparisons[3] = new More();
		comparisons[4] = new MoreOrEqual();
		comparisons[5] = new NotEqual();
		return comparisons;

	}
	private static Statement[] getStatements(){
		Statement[] statements = new Statement[9];
		statements[0] = new Add();
		statements[1] = new Assign();
		statements[2] = new Divide();
		statements[3] = new End();
		statements[4] = new For();
		statements[5] = new If();
		statements[6] = new Multiply();
		statements[7] = new Null();
		statements[8] = new Subtract();
		return statements;
	}

	private static Varpack getVarpack(){
		Variable[] writevars = new Variable[1];
		Variable[] readvars = new Variable[3];
		Variable[] lsize = new Variable[1];
		writevars[0] = new Variable("sum");
		lsize[0] = new Variable("list.size()");
		readvars[0] = new Variable("size");
		readvars[1] = new Variable("tmp");
		readvars[2] = new ListVariable("list", readvars[1]);
		return new Varpack(readvars, writevars, lsize, writevars[0]);
	}

	@Override
	public double fitness(Genotype g) {
		double fitness = 0;
		Statement[] st = makeProgram((WithallGenotype)g);
		Program p = new Program(st, returnVar, maxBlocks);
		for (Entry<int[], Integer> ent : samples.entrySet()){
			int[] list = ent.getKey();
			for (Variable v: readVars){
				v.setValue(0);
				v.init(list);
			}
			for (Variable v: writeVars){
				v.setValue(0);
				v.init(list);
			}

			for (Variable v: lSize){
				v.setValue(list.length);
				v.init(list);
			}
			int res = p.execute(/*this*/);

			fitness = fitness + Math.abs(res-ent.getValue());
//			System.out.println(res + " ... " + ent.getValue());
//			System.out.println(fitness);
		}
		if (fitness == 0) return Integer.MAX_VALUE;
		return 1/fitness;
	}

	@Override
	public void updateFitness(List<Genotype> population) {
	}


	public void printAllVars() {
		for (int i=0; i<writeVars.length; i++){
			System.out.print(writeVars[i].getObjectName() + " - " + writeVars[i].getValue());
			System.out.print("   ");
		}

		for (int i=0; i<readVars.length; i++){
			System.out.print(readVars[i].getObjectName() + " - " + readVars[i].getValue());
			System.out.print("   ");
		}

		for (int i=0; i<lSize.length; i++){
			System.out.print(lSize[i].getObjectName() + " - " + lSize[i].getValue());
			System.out.print("   ");
		}
	}

}

